import * as THREE from "../../../modules/three/three.module.js";
import dot_vertexGlsl from "../lib/shader/dot_vertex.glsl.js";
import grid_fragmentGlsl from "../lib/shader/grid_fragment.glsl.js";
import {
  bufferAttributeToVectors,
  flatten,
  getPolygonGeometry,
  vector3sToFloat32BufferAttribute
} from "../lib/util/StaticMethods.js";
import PathCompute, {boxArrFromMovementOfDrone, CornerComputeResult} from "./PathCompute.js";
import BoxFrameMesh from "../lib/gadget/BoxFrameMesh.js";
import {assets3dBaseUrl} from "../lib/CommonConsts.js";

function getGridMaterial(slashing) {
  const gridColor = new THREE.Vector4(0.0, 0.7, 0.1, 1.0);
  if (slashing) {
    gridColor.set(1.0, 0.1, 0.1, 1.0);
  }
  const uniforms = {
    gridColor: { value: gridColor },
    ratio: {
      value: new THREE.Vector2(
        100,
        (100 * window.innerHeight) / window.innerWidth,
      ),
    },
    densityAndSlash: { value: {x: 1.0, y: slashing ? 1.0: 0.0} },
  };
  const gridMaterial = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: dot_vertexGlsl,
    fragmentShader: grid_fragmentGlsl,
    side: THREE.DoubleSide,
    transparent: true,
  });
  return gridMaterial;
}

const cornerMtl = getGridMaterial(true);
const sprayMtl = getGridMaterial(false);

function addContourToMesh(mesh, lineColor='#fff') {
  const vectors = bufferAttributeToVectors(mesh.geometry.getAttribute('position'));
  const lineMat = new THREE.LineBasicMaterial({
    color: lineColor,
    toneMapped: false,
    transparent: true,
    depthTest: false,
    depthWrite: false,
  });
  const lineGeo = new THREE.BufferGeometry();
  lineGeo.setAttribute('position', new THREE.BufferAttribute(flatten(vectors), 3))
  const lineSegment = new THREE.LineLoop(lineGeo, lineMat);
  mesh.add(lineSegment);
}

const colorStart = '#0e308c';
const colorEnd = '#095e11';
const colorInvalid = '#f00';

function createDroneCollideIndicator(color) {
  const [boxGun, boxBody] = boxArrFromMovementOfDrone(null, 0);
  const frameGun = new BoxFrameMesh(color, false);
  const frameBody = new BoxFrameMesh(color, false);
  frameGun.update(boxGun);
  frameBody.update(boxBody);
  frameGun.add(frameBody);
  const size = boxGun.getSize(new THREE.Vector3());
  const hw = size.x * 0.35;
  const hh = size.y * 0.35;
  const p0 = new THREE.Vector3(-hw, 0, 0);
  const p1 = new THREE.Vector3(hw, 0, 0);
  const p2 = new THREE.Vector3(0, -hh, 0);
  const p3 = new THREE.Vector3(0, hh, 0);
  const lineGeo = new THREE.BufferGeometry()
  lineGeo.setAttribute('position', new THREE.BufferAttribute(flatten([p0, p1, p2, p3]), 3))
  const lineMat = new THREE.LineBasicMaterial({
    color: color,
    toneMapped: false,
    transparent: true,
    depthTest: false,
    depthWrite: false,
  })
  const lineSegment = new THREE.LineSegments(lineGeo, lineMat);
  frameGun.add(lineSegment);
  lineSegment.position.set(0, 0, -0.05);
  lineSegment.renderOrder = 20;
  return frameGun;
}

const droneStart = createDroneCollideIndicator(colorStart);
const droneEnd = createDroneCollideIndicator(colorEnd);
let settingStart = 1;
let selectWorkArea;
let pathCompute;
const pointMtl = new THREE.PointsMaterial({
  size: 16,
  color: '#093ea4',
  sizeAttenuation: false,
  map: new THREE.TextureLoader().load( assets3dBaseUrl + 'texture/circle.png' ),
  alphaTest: 0.5,
  toneMapped: false,
  transparent: true,
});
const colorLineDefault = '#ff6000';
const colorLineSelected = '#4b0175';
const lineMtl = new THREE.LineBasicMaterial({
  color: colorLineDefault,
  toneMapped: false,
  transparent: false,
});
let curPathIndicators = [];
const pathArr = [];
let curPathIndex = -1;
let curPosIndex = -1;

export default class PathComputeUI {
  static getDroneStartAndEnd() {
    return [droneStart, droneEnd];
  }

  static changeDroneCollideColor(droneIndicator, valid) {
    let color = droneIndicator === droneStart ? colorStart : colorEnd;
    if (!valid) {
      color = colorInvalid;
    }
    droneIndicator.traverse(obj3d => {
      if (obj3d.material) {
        obj3d.material.color.setStyle(color);
      }
    });
  }

  static pointerMove4SetStartEnd(world3d) {
    const scene = world3d.scene;
    const store = world3d.store;
    // selectWorkArea = undefined;
    if (!store.stateMovingStartEnd || settingStart === 0) {
      return;
    }
    const raycaster = world3d.mouseRaycaster;
    const intersects = raycaster.intersectObjects(store.areaMeshes, false);
    if (!intersects.length) {
      return;
    }
    const areaMesh = intersects[0].object;
    const vo = areaMesh.vo;
    selectWorkArea = areaMesh;
    const worldPos = intersects[0].point;
    const indicator = settingStart === 1 ? droneStart : droneEnd;
    indicator.position.copy(worldPos);
    indicator.rotation.setFromRotationMatrix(vo.planeMat);
    scene.add(indicator);
    if (!pathCompute) {
      const blockingArr = world3d.store.blockings.map(it => it.vo);
      const areaArr = world3d.store.areaMeshes.map(it => it.vo);
      pathCompute = new PathCompute(blockingArr, areaArr);
    }
    pathCompute.initBlockingBoxArr(vo);
    const valid = pathCompute.validateDronePosition(worldPos);
    PathComputeUI.changeDroneCollideColor(indicator, valid);
  }

  static pointerUp4SetStartEnd(world3d) {
    const store = world3d.store;
    if (!store.stateMovingStartEnd) {
      return;
    }
    settingStart++;
    settingStart = settingStart % 3;
  }

  static getSelectAreaMesh() {
    return selectWorkArea;
  }

  /**
   * 绘制搜索出的单条避障路线
   * @param {THREE.Vector3[]} path
   */
  static drawSinglePath(path) {
    const pts = path.map(point => point.clone().applyMatrix4(pathCompute.usingArea.planeMat));
    const pointsGeo = new THREE.BufferGeometry();
    const attr = vector3sToFloat32BufferAttribute(pts);
    pointsGeo.setAttribute("position", attr);
    const points = new THREE.Points(pointsGeo, pointMtl);
    const lineGeo = new THREE.BufferGeometry();
    lineGeo.setAttribute("position", attr);
    const lineSegments = new THREE.Line(lineGeo, lineMtl.clone());
    lineSegments.add(points);
    return lineSegments;
  }

  /**
   * 绘制搜索出的若干条避障路线
   * @param {THREE.Scene} scene
   * @param {DroneSprayPath[]} pathObjs
   */
  static displayDronePaths(scene, pathObjs) {
    const pathIndicators = [];
    pathArr.length = 0;
    pathObjs.forEach(pathObj => {
      pathArr.push(pathObj.path);
      const line = PathComputeUI.drawSinglePath(pathObj.path);
      pathIndicators.push(line);
      scene.add(line);
    });
    curPathIndicators = pathIndicators;
    curPathIndex = -1;
  }
  /**
   * 把 寻路路径的计算结果 从三维场景中清除
   * @param {WorkAreaMesh} workArea
   */
  static clearDroneSprayPaths(workArea) {
    curPathIndicators.forEach(it => it.removeFromParent());
    curPathIndicators.length = 0;
  }

  static moveStartDrone(isForward) {
    if (curPathIndex < 0 || curPathIndex > curPathIndicators.length - 1) {
      return;
    }
    isForward ? curPosIndex++ : curPosIndex--;
    const pointArr = pathArr[curPathIndex];
    if (curPosIndex < 0) {
      curPosIndex = pointArr.length - 1;
    } else if (curPosIndex > pointArr.length - 1) {
      curPosIndex = 0;
    }
    droneStart.position.copy(pointArr[curPosIndex]).applyMatrix4(pathCompute.usingArea.planeMat);
  }

  static resetCurPathIndex() {
    if (curPathIndex >= 0 && curPathIndex <= curPathIndicators.length - 1) {
      curPathIndicators[curPathIndex].material.color.setStyle(colorLineDefault);
      curPathIndicators[curPathIndex].material.transparent = false;
    }
    curPathIndex = -1;
    curPosIndex = -1;
  }
  static showSinglePath(isForward) {
    if (curPathIndex >= 0 && curPathIndex <= curPathIndicators.length - 1) {
      curPathIndicators[curPathIndex].material.color.setStyle(colorLineDefault);
      curPathIndicators[curPathIndex].material.transparent = false;
    }
    isForward ? curPathIndex++ : curPathIndex--;
    if (curPathIndex < 0) {
      curPathIndex = curPathIndicators.length - 1;
    } else if (curPathIndex > curPathIndicators.length - 1) {
      curPathIndex = 0;
    }
    curPathIndicators[curPathIndex].material.color.setStyle(colorLineSelected);
    curPathIndicators[curPathIndex].material.transparent = true;
  }

  /**
   * 把 喷涂死角区的计算结果 显示在场景中
   * @param {THREE.Scene} scene
   * @param {WorkAreaMesh} workArea
   * @param {CornerComputeResult|null} result
   */
  static displayCornersAndAreas(scene, workArea, result= null) {
    const cornerMeshes = [];
    const sprayMeshes = [];
    const vo = workArea.vo;
    let cornerResult = result;
    if (!cornerResult) {
      cornerResult = new CornerComputeResult(workArea);
    }
    cornerResult.corners.forEach(obj => {
      const geometry = getPolygonGeometry(obj, vo.planeMat, vo.facing);
      const mesh = new THREE.Mesh(geometry, cornerMtl);
      cornerMeshes.push(mesh);
      scene.add(mesh);
      addContourToMesh(mesh, '#d21111');
    });
    cornerResult.sprayAreas.forEach(obj => {
      const geometry = getPolygonGeometry(obj, vo.planeMat, vo.facing);
      const mesh = new THREE.Mesh(geometry, sprayMtl);
      sprayMeshes.push(mesh);
      scene.add(mesh);
      addContourToMesh(mesh, '#0f6e22');
    });
    workArea.userData.cornerMeshes = cornerMeshes;
    workArea.userData.sprayMeshes = sprayMeshes;
  }

  /**
   * 把 喷涂死角区的计算结果 从三维场景中清除
   * @param {MainStore} store
   */
  static clearCornersAndAreas(store) {
    store.areaMeshes.forEach(workArea => {
      if (workArea.userData.cornerMeshes) {
        workArea.userData.cornerMeshes.forEach(it => it.removeFromParent());
        workArea.userData.cornerMeshes.length = 0;
      }
      if (workArea.userData.sprayMeshes) {
        workArea.userData.sprayMeshes.forEach(it => it.removeFromParent());
        workArea.userData.sprayMeshes.length = 0;
      }
    });
  }
}
